package models

import (
	"context"
	"fmt"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/ethclient"
	_ "github.com/ethereum/go-ethereum/ethclient"
	"go-chain-data/global"
	"go-chain-data/utils"
	"go.mongodb.org/mongo-driver/bson"
	"log"
	"math/big"
)

type Token struct {
	ChainName   string `bson:"chainName"`
	ChainId     uint   `bson:"chainId"`
	Address     string `bson:"address"`     // 合约地址，Ethereum地址长度为42个字符
	Name        string `bson:"name"`        //名称
	Symbol      string `bson:"symbol"`      //符号
	Decimals    uint8  `bson:"decimals"`    //精度
	TotalSupply string `bson:"totalSupply"` //总发行量
}

func SaveTokenPari(client *ethclient.Client, l *types.Log, chainName string, chainId uint) error {
	swapInfo, err := GetSwapInfoByAddress(l.Address.String())
	if swapInfo.Address != "" {
		return nil
	}
	//找到pair地址
	token0, err := utils.CallContractMethod(client, l.Address, utils.UNISWAP_V2_PAIR_ABI, "token0")
	if err != nil {
		log.Printf("failed to get token: %v", err)
		return err
	}
	token1, err := utils.CallContractMethod(client, l.Address, utils.UNISWAP_V2_PAIR_ABI, "token1")
	if err != nil {
		log.Printf("failed to get token: %v", err)
		return err
	}
	factory, err := utils.CallContractMethod(client, l.Address, utils.UNISWAP_V2_PAIR_ABI, "factory")
	if err != nil {
		log.Printf("failed to get token: %v", err)
		return err
	}
	name0, err := utils.CallContractMethod(client, token0[0].(common.Address), utils.ERC20_ABI, "name")
	if err != nil {
		log.Printf("failed to get token: %v", err)
		return err
	}
	symbol0, err := utils.CallContractMethod(client, token0[0].(common.Address), utils.ERC20_ABI, "symbol")
	if err != nil {
		log.Printf("failed to get token: %v", err)
		return err
	}
	decimals0, err := utils.CallContractMethod(client, token0[0].(common.Address), utils.ERC20_ABI, "decimals")
	if err != nil {
		log.Printf("failed to get token: %v", err)
		return err
	}

	name1, err := utils.CallContractMethod(client, token1[0].(common.Address), utils.ERC20_ABI, "name")
	if err != nil {
		log.Printf("failed to get token: %v", err)
		return err
	}
	symbol1, err := utils.CallContractMethod(client, token1[0].(common.Address), utils.ERC20_ABI, "symbol")
	if err != nil {
		log.Printf("failed to get token: %v", err)
		return err
	}
	decimals1, err := utils.CallContractMethod(client, token1[0].(common.Address), utils.ERC20_ABI, "decimals")
	if err != nil {
		log.Printf("failed to get token: %v", err)
		return err
	}
	totalSupply0, err := utils.CallContractMethod(client, token0[0].(common.Address), utils.ERC20_ABI, "totalSupply")
	if err != nil {
		log.Printf("failed to get token: %v", err)
		return err
	}
	totalSupply1, err := utils.CallContractMethod(client, token1[0].(common.Address), utils.ERC20_ABI, "totalSupply")
	if err != nil {
		log.Printf("failed to get token: %v", err)
		return err
	}
	tokenData0 := Token{
		ChainName:   chainName,
		ChainId:     chainId,
		Address:     token0[0].(common.Address).String(),
		Name:        name0[0].(string),
		Symbol:      symbol0[0].(string),
		Decimals:    decimals0[0].(uint8),
		TotalSupply: totalSupply0[0].(*big.Int).String(),
	}
	_, err = GetTokenInfoByAddress(tokenData0.Address)
	if err != nil && "mongo: no documents in result" == err.Error() {
		err = CreateTokenInfo(tokenData0)
		if err != nil {
			log.Printf("insert token表失败: %v", err)
			return err
		}
	}
	tokenData1 := Token{
		ChainId:     chainId,
		ChainName:   chainName,
		Address:     token1[0].(common.Address).String(),
		Name:        name1[0].(string),
		Symbol:      symbol1[0].(string),
		Decimals:    decimals1[0].(uint8),
		TotalSupply: totalSupply1[0].(*big.Int).String(),
	}
	_, err = GetTokenInfoByAddress(tokenData1.Address)
	if err != nil && "mongo: no documents in result" == err.Error() {
		err = CreateTokenInfo(tokenData1)
		if err != nil {
			log.Printf("failed to get token: %v", err)
			return err
		}
	}
	pair := &SwapInfo{
		Address:       l.Address.String(),
		Token0Address: token0[0].(common.Address).String(),
		Token1Address: token1[0].(common.Address).String(),
		Token0Name:    symbol0[0].(string),
		Token1Name:    symbol1[0].(string),
		Factory:       factory[0].(common.Address).String(),
		ChainId:       chainId,
		ChainName:     chainName,
	}
	err = CreateSwapInfo(pair)
	if err != nil {
		return err
	}
	return nil
}

func GetTokenInfoByAddress(address string) (Token, error) {
	tokenClient := global.DBEngine.Database(global.DbConfig.DbName).Collection("token")
	filter := bson.D{{"address", address}}
	var token Token
	err := tokenClient.FindOne(context.Background(), filter).Decode(&token)
	return token, err
}

func CreateTokenInfo(token Token) error {
	tokenClient := global.DBEngine.Database(global.DbConfig.DbName).Collection("token")
	_, err := tokenClient.InsertOne(context.Background(), token)
	if err != nil {
		return err
	}
	//fmt.Println("插入token表成功：", one.InsertedID)
	fmt.Println("插入token表成功：", token.Symbol)
	return nil
}
